package com.fzk.utils;

import com.alibaba.fastjson2.JSON;
import com.fzk.log.Logger;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author uestcfzk
 * @date 2025-02-26 22-42-10
 */

@Data
@NoArgsConstructor// 这个必须有，fastjson没找到无参构造器居然用唯一构造器且从json串中找对应参数，我尼玛的服了呀，而不是报错?
public class MyToken {
    // note: 启动的时候生成秘钥扰动，保证token的sign不可推测，缺点是只能单机使用，且重启将重置秘钥
    private static final String salt;
    private Map<String, Object> header;
    private Map<String, Object> payload;
    // sb fastjson 解析long类型精度丢失，还没法通过配置简单解决，评价是不如go sonic一根毛
    private String createTime;// 时间戳-毫秒级
    private String expireTime;// 时间戳-毫秒级
    private String sign;

    static {
        salt = MyRandomUtil.randomStr(16);
        Logger.info("random salt of myToken is " + salt);
    }

    public MyToken(long expire) {
        this.createTime = String.valueOf(System.currentTimeMillis());
        this.expireTime = String.valueOf(System.currentTimeMillis() + expire);
    }

    public MyToken addHeader(String name, Object value) {
        if (header == null) {
            header = new HashMap<>();
        }
        header.put(name, value);
        return this;
    }

    public MyToken addPayload(String name, Object value) {
        if (payload == null) {
            payload = new HashMap<>();
        }
        payload.put(name, value);
        return this;
    }

    public static String calculateSign(MyToken myToken) {
        return MyHmac.hmacMd5(String.format("%s_%s_%s_%s", myToken.getHeader(), myToken.getPayload(), myToken.getCreateTime(), myToken.getExpireTime()).getBytes(StandardCharsets.UTF_8), salt);
    }

    public String toTokenString() {
        if (StringUtils.isEmpty(this.sign)) {
            this.sign = calculateSign(this);
        }
        // json序列化后转base64
        return Base64.getEncoder().encodeToString(JSON.toJSONString(this).getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 仅校验token本身值的合法性: 未过期且签名合法
     *
     * @param token 登录令牌
     * @return null表示校验失败, 非null表示token校验成功
     */
    public static MyToken validate(String token) {
        if (StringUtils.isEmpty(token)) return null;
        try {
            // 反序列化
            MyToken myToken = JSON.parseObject(Base64.getDecoder().decode(token), MyToken.class);
            // 校验过期时间
            if (Long.parseLong(myToken.createTime) <= System.currentTimeMillis() && Long.parseLong(myToken.expireTime) >= System.currentTimeMillis()) {
                // 校验签名sign
                if (myToken.sign != null && myToken.sign.equals(MyToken.calculateSign(myToken))) {
                    return myToken;
                }
            }
        } catch (RuntimeException e) {
            Logger.warning(String.format("validate token occurs exception: %s", e));
        }
        return null;
    }
}
